package com.rethinkdb.gen;

import com.rethinkdb.RethinkDB;
import com.rethinkdb.gen.exc.*;
import com.rethinkdb.gen.ast.*;
import com.rethinkdb.ast.ReqlAst;
import com.rethinkdb.model.MapObject;
import com.rethinkdb.model.OptArgs;
import com.rethinkdb.net.Connection;
import com.rethinkdb.net.Cursor;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.junit.*;
import org.junit.rules.ExpectedException;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.Instant;
import java.util.stream.LongStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.concurrent.TimeoutException;
import java.util.regex.Pattern;
import java.util.Collections;
import java.nio.charset.StandardCharsets;

import static com.rethinkdb.TestingCommon.*;
import com.rethinkdb.TestingFramework;

public class ${module_name} {
    // ${description}
    Logger logger = LoggerFactory.getLogger(${module_name}.class);
    public static final RethinkDB r = RethinkDB.r;
    %for var_name in table_var_names:
    public static final Table ${var_name} = r.db("test").table("${var_name}");
    %endfor

    Connection conn;

    @Before
    public void setUp() throws Exception {
        logger.info("Setting up.");
        conn = TestingFramework.createConnection();
        try {
            r.dbCreate("test").run(conn);
            r.db("test").wait_().run(conn);
        }catch (Exception e){}
        %for var_name in table_var_names:
        try {
            r.db("test").tableCreate("${var_name}").run(conn);
            r.db("test").table(${var_name}).wait_().run(conn);
        }catch (Exception e){}
        %endfor
    }

    @After
    public void tearDown() throws Exception {
        logger.info("Tearing down.");
        r.db("rethinkdb").table("_debug_scratch").delete().run(conn);
        if(!conn.isOpen()){
            conn.close();
            conn = TestingFramework.createConnection();
        }
        %for var_name in table_var_names:
        r.db("test").tableDrop("${var_name}").run(conn);
        %endfor
        r.dbDrop("test").run(conn);
        conn.close(false);
    }

    // Autogenerated tests below

<%rendered_vars = set() %>\
    @Test(timeout=120000)
    public void test() throws Exception {
<%rendered_something = False %>\
        %for item in defs_and_test:
        %if type(item) == JavaDef:
<%rendered_something = True %>
        // ${item.testfile} line #${item.line_num}
        // ${item.line.original}
        logger.info("Possibly executing: ${item.line.java.replace('\\', '\\\\').replace('"', "'")}");
        %if item.varname in rendered_vars:
        ${item.varname} = ${"maybeRun" if item.run_if_query else ""}(${item.value});
        %elif item.run_if_query:
          %if item.runopts:
        Object ${item.varname} = maybeRun(${item.value}, conn, new OptArgs()
              %for key, val in item.runopts.items():
                                          .with("${key}", ${val})
              %endfor
              );
          %else:
        Object ${item.varname} = maybeRun(${item.value}, conn);
<%rendered_vars.add(item.varname)%>\
          %endif
        %else:
        ${item.vartype} ${item.varname} = ${item.value};
<%rendered_vars.add(item.varname)%>\
        %endif
        %elif type(item) == JavaQuery:
<%rendered_something = True %>
        {
            // ${item.testfile} line #${item.line_num}
            /* ${item.expected_line.original} */
            ${item.expected_type} expected_ = ${item.expected_line.java};
            /* ${item.line.original} */
            logger.info("About to run line #${item.line_num}: ${item.line.java.replace('"', "'").replace('\\', '\\\\').replace('\n', '\\n')}");
            Object obtained = runOrCatch(${item.line.java},
                                          new OptArgs()
            %if item.runopts:
              %for key, val in item.runopts.items():
                                          .with("${key}", ${val})
              %endfor
            %endif
                                          ,conn);
            try {
            %if item.expected_type.endswith('[]'):
                assertArrayEquals(expected_, (${item.expected_type}) obtained);
            %elif item.expected_type == 'Double':
                assertEquals((double) expected_,
                             ((Number) obtained).doubleValue(),
                             0.00000000001);
            %else:
                assertEquals(expected_, obtained);
            %endif
            logger.info("Finished running line #${item.line_num}");
            } catch (Throwable ae) {
                logger.error("Whoops, got exception on line #${item.line_num}:" + ae.toString());
                if(obtained instanceof Throwable) {
                    ae.addSuppressed((Throwable) obtained);
                }
                throw ae;
            }
        }
        %endif
        %endfor
        %if not rendered_something:
<% raise EmptyTemplate() %>\
        %endif
    }
}
